#pragma once

#include "MyRefPtr.h"

namespace my
{
	enum class LogLevel
	{
		Verbose = 1,
		Debug,
		Info,
		Warning,
		Error,
		Fatal,
	};


	class LogOutput : public Ref
	{
	public:
		virtual void write(const char *name, LogLevel level, const char *text, size_t text_length) = 0;
	};


	class LogSystem : public Singleton<LogSystem>
	{
	public:
		void add_output(Ptr<LogOutput> &&output) { m_outputs.emplace_back(output); }

#if defined(_MSC_VER)
		void write_f(const char *name, LogLevel minimal_level, LogLevel current_level, _In_z_ _Printf_format_string_ const char *format, ...);
#else
		void write_f(const char *name, LogLevel minimal_level, LogLevel current_level, const char *format, ...);
#endif

		static const char *get_level_name(LogLevel level);

	private:
		void write_to_output(const char *name, LogLevel level, const char *text, size_t text_length);

		std::vector<Ptr<LogOutput>> m_outputs;
	};
}

#define MY_LOG_LOCAL_SETUP(name, level) \
	static const char *s_my_log_name = (name); \
	static const my::LogLevel s_my_log_level = (level)

#define MY_LOG_VERBOSE(fmt, ...)	my::LogSystem::instance().write_f(s_my_log_name, s_my_log_level, my::LogLevel::Verbose, fmt, __VA_ARGS__)
#define MY_LOG_DEBUG(fmt, ...)		my::LogSystem::instance().write_f(s_my_log_name, s_my_log_level, my::LogLevel::Debug, fmt, __VA_ARGS__)
#define MY_LOG_INFO(fmt, ...)		my::LogSystem::instance().write_f(s_my_log_name, s_my_log_level, my::LogLevel::Info, fmt, __VA_ARGS__)
#define MY_LOG_WARNING(fmt, ...)	my::LogSystem::instance().write_f(s_my_log_name, s_my_log_level, my::LogLevel::Warning, fmt, __VA_ARGS__)
#define MY_LOG_ERROR(fmt, ...)		my::LogSystem::instance().write_f(s_my_log_name, s_my_log_level, my::LogLevel::Error, fmt, __VA_ARGS__)
#define MY_LOG_FATAL(fmt, ...)		my::LogSystem::instance().write_f(s_my_log_name, s_my_log_level, my::LogLevel::Fatal, fmt, __VA_ARGS__)
